From: Ray Strode Date: Thu, 21 Oct 2010 00:46:38 +0000 (-0400) Subject: modalDialog: Add modal dialog base class X-Git-Tag: archive/raspbian/50.5-1+rpi1~1^2~1013^2^2~9274 X-Git-Url: https://dgit.raspbian.org/%22http://www.example.com/cgi/%22/%22http:/www.example.com/cgi/%22?a=commitdiff_plain;h=47b8d16067180cb5c7ad86efb99819ad466312a4;p=gnome-shell.git modalDialog: Add modal dialog base class This is a base class to make it easier to gain a consistent look for system modal dialogs. It handles creating a darkened backdrop behind the dialog, setting up buttons in the dialog, keynav, etc. https://bugzilla.gnome.org/show_bug.cgi?id=637187 --- diff --git a/data/theme/gnome-shell.css b/data/theme/gnome-shell.css index 344692af7..1240c12cb 100644 --- a/data/theme/gnome-shell.css +++ b/data/theme/gnome-shell.css @@ -1080,6 +1080,54 @@ StTooltip StLabel { border-radius: 8px; } +/* Modal Dialogs */ +.modal-dialog { + font: 12pt sans-serif; + border-radius: 24px; + background-color: rgba(0.0, 0.0, 0.0, 0.9); + border: 2px solid #868686; + color: #ffffff; + + padding-right: 42px; + padding-left: 42px; + padding-bottom: 30px; + padding-top: 30px; +} + +.modal-dialog-button { + border: 1px solid #8b8b8b; + border-radius: 18px; + font: 14px sans-serif; + + margin-left: 10px; + margin-right: 10px; + + padding-left: 32px; + padding-right: 32px; + padding-top: 8px; + padding-bottom: 8px; + + background-gradient-direction: vertical; + background-gradient-start: #29323b; + background-gradient-end: #121a24; +} + +.modal-dialog-button:active, +.modal-dialog-button:pressed { + border-color: #a5a5a5; + background-gradient-start: #121a24; + background-gradient-end: #29323b; +} + +.modal-dialog-button:focus { + border: 2px solid #a5a5a5; + + padding-left: 31px; + padding-right: 31px; + padding-top: 7px; + padding-bottom: 7px; +} + /* Run Dialog */ .run-dialog-label { font: 12px sans-serif; diff --git a/js/Makefile.am b/js/Makefile.am index 3d87decc9..3dce4f292 100644 --- a/js/Makefile.am +++ b/js/Makefile.am @@ -32,6 +32,7 @@ nobase_dist_js_DATA = \ ui/magnifierDBus.js \ ui/main.js \ ui/messageTray.js \ + ui/modalDialog.js \ ui/notificationDaemon.js \ ui/overview.js \ ui/panel.js \ diff --git a/js/ui/modalDialog.js b/js/ui/modalDialog.js new file mode 100644 index 000000000..7b3f0d36f --- /dev/null +++ b/js/ui/modalDialog.js @@ -0,0 +1,233 @@ +/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ + +const Clutter = imports.gi.Clutter; +const Gdk = imports.gi.Gdk; +const Gio = imports.gi.Gio; +const GLib = imports.gi.GLib; +const Lang = imports.lang; +const Meta = imports.gi.Meta; +const Pango = imports.gi.Pango; +const St = imports.gi.St; +const Shell = imports.gi.Shell; +const Signals = imports.signals; +const Gettext = imports.gettext.domain('gnome-shell'); +const _ = Gettext.gettext; + +const Params = imports.misc.params; + +const Lightbox = imports.ui.lightbox; +const Main = imports.ui.main; +const Tweener = imports.ui.tweener; + +const OPEN_AND_CLOSE_TIME = 0.1; +const FADE_OUT_DIALOG_TIME = 1.0; + +const State = { + OPENED: 0, + CLOSED: 1, + OPENING: 2, + CLOSING: 3, + FADED_OUT: 4 +}; + +function ModalDialog() { + this._init(); +} + +ModalDialog.prototype = { + _init: function(params) { + params = Params.parse(params, { styleClass: null }); + + this.state = State.CLOSED; + + this._group = new St.Group({ visible: false, + x: 0, + y: 0 }); + Main.uiGroup.add_actor(this._group); + global.focus_manager.add_group(this._group); + this._initialKeyFocus = this._group; + + this._group.connect('destroy', Lang.bind(this, this._onGroupDestroy)); + + this._actionKeys = {}; + this._group.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); + + this._lightbox = new Lightbox.Lightbox(this._group, + { inhibitEvents: true }); + + this._backgroundBin = new St.Bin(); + + this._group.add_actor(this._backgroundBin); + this._lightbox.highlight(this._backgroundBin); + + this._dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', + vertical: true }); + if (params.styleClass != null) { + this._dialogLayout.add_style_class_name(params.styleClass); + } + this._backgroundBin.child = this._dialogLayout; + + this.contentLayout = new St.BoxLayout({ vertical: true }); + this._dialogLayout.add(this.contentLayout, + { x_fill: true, + y_fill: true, + x_align: St.Align.MIDDLE, + y_align: St.Align.START }); + + this._buttonLayout = new St.BoxLayout({ opacity: 220, + vertical: false }); + this._dialogLayout.add(this._buttonLayout, + { expand: true, + x_align: St.Align.MIDDLE, + y_align: St.Align.END }); + }, + + setButtons: function(buttons) { + this._buttonLayout.remove_all(); + let i = 0; + for (let index in buttons) { + let buttonInfo = buttons[index]; + let label = buttonInfo['label']; + let action = buttonInfo['action']; + let key = buttonInfo['key']; + + let button = new St.Button({ style_class: 'modal-dialog-button', + reactive: true, + can_focus: true, + label: label }); + + let x_alignment; + if (buttons.length == 1) + x_alignment = St.Align.END; + else if (i == 0) + x_alignment = St.Align.START; + else if (i == buttons.length - 1) + x_alignment = St.Align.END; + else + x_alignment = St.Align.MIDDLE; + + this._initialKeyFocus = button; + this._buttonLayout.add(button, + { expand: true, + x_fill: false, + y_fill: false, + x_align: x_alignment, + y_align: St.Align.MIDDLE }); + + button.connect('clicked', action); + + if (key) + this._actionKeys[key] = action; + i++; + } + }, + + _onKeyPressEvent: function(object, keyPressEvent) { + let symbol = keyPressEvent.get_key_symbol(); + let action = this._actionKeys[symbol]; + + if (action) + action(); + }, + + _onGroupDestroy: function() { + this.emit('destroy'); + }, + + _fadeOpen: function() { + let monitor = global.get_focus_monitor(); + + this._backgroundBin.set_position(monitor.x, monitor.y); + this._backgroundBin.set_size(monitor.width, monitor.height); + + this.state = State.OPENING; + + this._dialogLayout.opacity = 255; + this._lightbox.show(); + this._group.opacity = 0; + this._group.show(); + Tweener.addTween(this._group, + { opacity: 255, + time: OPEN_AND_CLOSE_TIME, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, + function() { + this._initialKeyFocus.grab_key_focus(); + this.state = State.OPENED; + this.emit('opened'); + }), + }); + }, + + open: function(timestamp) { + if (this.state == State.OPENED || this.state == State.OPENING) + return true; + + if (!Main.pushModal(this._group, timestamp)) + return false; + + global.stage.set_key_focus(this._group); + + this._fadeOpen(); + return true; + }, + + close: function(timestamp) { + if (this.state == State.CLOSED || this.state == State.CLOSING) + return; + + let needsPopModal; + + if (this.state == State.OPENED || this.state == State.OPENING) + needsPopModal = true; + else + needsPopModal = false; + + this.state = State.CLOSING; + + Tweener.addTween(this._group, + { opacity: 0, + time: OPEN_AND_CLOSE_TIME, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, + function() { + this.state = State.CLOSED; + this._group.hide(); + + if (needsPopModal) + Main.popModal(this._group, timestamp); + }) + }); + }, + + // This method is like close, but fades the dialog out much slower, + // and leaves the lightbox in place. Once in the faded out state, + // the dialog can be brought back by an open call, or the lightbox + // can be dismissed by a close call. + // + // The main point of this method is to give some indication to the user + // that the dialog reponse has been acknowledged but will take a few + // moments before being processed. + // e.g., if a user clicked "Log Out" then the dialog should go away + // imediately, but the lightbox should remain until the logout is + // complete. + _fadeOutDialog: function(timestamp) { + if (this.state == State.CLOSED || this.state == State.CLOSING) + return; + + if (this.state == State.FADED_OUT) + return; + + Tweener.addTween(this._dialogLayout, + { opacity: 0, + time: FADE_OUT_DIALOG_TIME, + transition: 'easeOutQuad', + onComplete: Lang.bind(this, + function() { + this.state = State.FADED_OUT; + Main.popModal(this._group, timestamp); + }) + }); + } +}; +Signals.addSignalMethods(ModalDialog.prototype);